﻿using KnBinarySerializer.Field;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace KnBinarySerializer
{
    internal class EnumerableFormatter : IFormatter
    {
        public Type GetHandleType()
        {
            return null;
        }

        public byte GetKey()
        {
            return (byte)1;
        }
        public bool IsFixLen()
        {
            return false;
        }

        static List<Type> _types = new List<Type>();
        static EnumerableFormatter()
        {
            _types.Add(typeof(Array));
            _types.Add(typeof(List<>));
        }

        private static (byte,byte) getListTypeKey(BinarySerializeManager content,Type arrType)
        {
            byte listKey = 0;
            if (arrType.IsArray)
                listKey = 0;
            else
                listKey = 1;
            byte arrKey = 0;
            Type elemType = getElementType(arrType);
            if (elemType != null)
            {
                IFormatter formatter = content.GetFormatter(elemType);
                arrKey = formatter.GetKey();
            }
            return (listKey, arrKey);
        }

        private static Type getElementType(Type arrType)
        {
            if (arrType.IsArray)
                return arrType.GetElementType();

            Type[] sub = arrType.GetGenericArguments();
            if (sub.Length == 0)
                return null;
            return sub[0];
        }
        private IKnArray gencArray(byte listKey, byte elementKey, int count, out Type subType)
        {
            IFormatter formatter = contentContex.GetFormatter(elementKey);
            subType = formatter.GetHandleType();
            if (listKey == 0)
            {               
                return new KnArray(Array.CreateInstance(subType, count));
            }
            else
            {
                Type type = _types[(int)listKey];
                Type listType = type.MakeGenericType(subType);
                return new KnArrayList((IList)ClassSerialContext.CreateObject(listType));
            }
        }
        private IKnArray gencArray(Type type,int count, out Type subType)
        {
            if (type.BaseType.Name == "Array")
            {
                subType = type.GetElementType();
                return new KnArray(Array.CreateInstance(subType, count));
            }
            else
            {
                subType = getArgumentType(type);
                return new KnArrayList((IList)ClassSerialContext.CreateObject(type));
            }
        }
        private Type getArgumentType(Type type)
        {
            Type[] itemTypes = type.GetGenericArguments();
            if (itemTypes.Length == 0)
                return getArgumentType(type.BaseType);
            else
                return itemTypes[0];
        }

        BinarySerializeManager contentContex;
        public object ReadBytes(BinarySerializeManager content, int maxLen, Type type)
        {
            contentContex = content;
            //读取类型
            byte listKey = content.ReadByte();
            byte elemKey = content.ReadByte();
            //读取个数
            int count = content.ReadInt();
            Type subType;
            IKnArray list = null;
            if (type == null)
                list = gencArray(listKey, elemKey, count, out subType);
            else
                list = gencArray(type, count, out subType);
                       
            int index = 0;
            while(index < count)
            {
                IFormatter formater = content.GetFormatter(content.ReadByte());
                index ++;                
                //byte[] subContent = new byte[len];
                //Array.Copy(content, index, subContent, 0, len);
                object item = formater.ReadBytes(content,0, subType);                
                list.Add(item);                
            }
            return list.GetArray();
        }        

        public int WriteBytes(BinarySerializeManager serial, object obj)
        {
            //写入Array/List及ElementType
            (byte listKey,byte elemKey) = getListTypeKey(serial,obj.GetType());
            serial.Write(listKey);
            serial.Write(elemKey);
            //IEnumerable list = (IEnumerable)obj;
            IList list = (IList)obj;            
            //写入个数
            serial.Write(list.Count);
            foreach (object item in list)
            {
                serial.SerializeObject(item);
            }
            return -1;
        }
    }

    public interface IKnArray
    {
        void Add(object obj);

        object GetArray();
    }

    public class KnArray : IKnArray
    {
        private Array array;
        private int index;
        public KnArray(Array val)
        {
            array = val;
            index = 0;
        }
        public void Add(object obj)
        {
            array.SetValue(obj, index++);
        }
        public object GetArray()
        {
            return array;
        }
    }
    public class KnArrayList : IKnArray
    {
        private IList array;
        public KnArrayList(IList list)
        {
            array = list;
        }
        public void Add(object obj)
        {
            array.Add(obj);
        }

        public object GetArray()
        {
            return array;
        }
    }
}
